import { IsString, IsNumber, IsOptional, IsArray, IsBoolean, IsEnum, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';

export enum GeminiRole {
  USER = 'user',
  MODEL = 'model'
}

export enum GeminiHarmCategory {
  HARM_CATEGORY_UNSPECIFIED = 'HARM_CATEGORY_UNSPECIFIED',
  HARM_CATEGORY_DEROGATORY = 'HARM_CATEGORY_DEROGATORY',
  HARM_CATEGORY_TOXICITY = 'HARM_CATEGORY_TOXICITY',
  HARM_CATEGORY_VIOLENCE = 'HARM_CATEGORY_VIOLENCE',
  HARM_CATEGORY_SEXUAL = 'HARM_CATEGORY_SEXUAL',
  HARM_CATEGORY_MEDICAL = 'HARM_CATEGORY_MEDICAL',
  HARM_CATEGORY_DANGEROUS = 'HARM_CATEGORY_DANGEROUS',
  HARM_CATEGORY_HARASSMENT = 'HARM_CATEGORY_HARASSMENT',
  HARM_CATEGORY_HATE_SPEECH = 'HARM_CATEGORY_HATE_SPEECH',
  HARM_CATEGORY_SEXUALLY_EXPLICIT = 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
  HARM_CATEGORY_DANGEROUS_CONTENT = 'HARM_CATEGORY_DANGEROUS_CONTENT'
}

export enum GeminiHarmBlockThreshold {
  HARM_BLOCK_THRESHOLD_UNSPECIFIED = 'HARM_BLOCK_THRESHOLD_UNSPECIFIED',
  BLOCK_LOW_AND_ABOVE = 'BLOCK_LOW_AND_ABOVE',
  BLOCK_MEDIUM_AND_ABOVE = 'BLOCK_MEDIUM_AND_ABOVE',
  BLOCK_ONLY_HIGH = 'BLOCK_ONLY_HIGH',
  BLOCK_NONE = 'BLOCK_NONE'
}

export enum GeminiFinishReason {
  FINISH_REASON_UNSPECIFIED = 'FINISH_REASON_UNSPECIFIED',
  STOP = 'STOP',
  MAX_TOKENS = 'MAX_TOKENS',
  SAFETY = 'SAFETY',
  RECITATION = 'RECITATION',
  OTHER = 'OTHER'
}

export class GeminiPart {
  @ApiPropertyOptional({ description: 'The text content' })
  @IsString()
  @IsOptional()
  text?: string;

  @ApiPropertyOptional({ description: 'Inline media bytes' })
  @IsOptional()
  inlineData?: {
    mimeType: string;
    data: string;
  };

  @ApiPropertyOptional({ description: 'A predicted FunctionCall returned from the model' })
  @IsOptional()
  functionCall?: {
    name: string;
    args: Record<string, any>;
  };

  @ApiPropertyOptional({ description: 'The result output of a FunctionCall' })
  @IsOptional()
  functionResponse?: {
    name: string;
    response: Record<string, any>;
  };
}

export class GeminiContent {
  @ApiProperty({ type: [GeminiPart], description: 'Ordered Parts that constitute a single message' })
  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => GeminiPart)
  parts: GeminiPart[];

  @ApiPropertyOptional({ enum: GeminiRole, description: 'The producer of the content' })
  @IsEnum(GeminiRole)
  @IsOptional()
  role?: GeminiRole;
}

export class GeminiSafetySettings {
  @ApiProperty({ enum: GeminiHarmCategory, description: 'The category for this setting' })
  @IsEnum(GeminiHarmCategory)
  category: GeminiHarmCategory;

  @ApiProperty({ enum: GeminiHarmBlockThreshold, description: 'Controls the probability threshold at which harm is blocked' })
  @IsEnum(GeminiHarmBlockThreshold)
  threshold: GeminiHarmBlockThreshold;
}

export class GeminiGenerationConfig {
  @ApiPropertyOptional({ description: 'Optional. The set of character sequences that will stop output generation' })
  @IsArray()
  @IsString({ each: true })
  @IsOptional()
  stopSequences?: string[];

  @ApiPropertyOptional({ description: 'Optional. Controls the randomness of the output', minimum: 0, maximum: 1 })
  @IsNumber()
  @IsOptional()
  temperature?: number;

  @ApiPropertyOptional({ description: 'Optional. The maximum cumulative probability of tokens to consider when sampling', minimum: 0, maximum: 1 })
  @IsNumber()
  @IsOptional()
  topP?: number;

  @ApiPropertyOptional({ description: 'Optional. The maximum number of tokens to consider when sampling', minimum: 1 })
  @IsNumber()
  @IsOptional()
  topK?: number;

  @ApiPropertyOptional({ description: 'Optional. The number of generated responses to return', minimum: 1 })
  @IsNumber()
  @IsOptional()
  candidateCount?: number;

  @ApiPropertyOptional({ description: 'Optional. The maximum number of tokens to generate' })
  @IsNumber()
  @IsOptional()
  maxOutputTokens?: number;
}

export class GeminiFunctionDeclaration {
  @ApiProperty({ description: 'The name of the function' })
  @IsString()
  name: string;

  @ApiProperty({ description: 'A brief description of the function' })
  @IsString()
  description: string;

  @ApiPropertyOptional({ description: 'Describes the parameters to this function' })
  @IsOptional()
  parameters?: {
    type: string;
    properties: Record<string, any>;
    required?: string[];
  };
}

export class GeminiTool {
  @ApiProperty({ type: [GeminiFunctionDeclaration], description: 'A list of FunctionDeclarations available to the model' })
  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => GeminiFunctionDeclaration)
  functionDeclarations: GeminiFunctionDeclaration[];
}

export class GeminiGenerateContentRequest {
  @ApiProperty({ type: [GeminiContent], description: 'The content of the current conversation with the model' })
  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => GeminiContent)
  contents: GeminiContent[];

  @ApiPropertyOptional({ type: [GeminiTool], description: 'A list of Tools the model may use to generate the next response' })
  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => GeminiTool)
  @IsOptional()
  tools?: GeminiTool[];

  @ApiPropertyOptional({ type: [GeminiSafetySettings], description: 'A list of unique SafetySettings instances for blocking unsafe content' })
  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => GeminiSafetySettings)
  @IsOptional()
  safetySettings?: GeminiSafetySettings[];

  @ApiPropertyOptional({ type: GeminiContent, description: 'Developer set system instruction' })
  @ValidateNested()
  @Type(() => GeminiContent)
  @IsOptional()
  systemInstruction?: GeminiContent;

  @ApiPropertyOptional({ type: GeminiGenerationConfig, description: 'Configuration options for model generation and outputs' })
  @ValidateNested()
  @Type(() => GeminiGenerationConfig)
  @IsOptional()
  generationConfig?: GeminiGenerationConfig;
}

export class GeminiSafetyRating {
  @ApiProperty({ enum: GeminiHarmCategory, description: 'The category for this rating' })
  category: GeminiHarmCategory;

  @ApiProperty({ description: 'The probability of harm for this content' })
  probability: string;
}

export class GeminiCandidate {
  @ApiProperty({ type: GeminiContent, description: 'Generated content returned from the model' })
  content: GeminiContent;

  @ApiProperty({ enum: GeminiFinishReason, description: 'The reason why the model stopped generating tokens' })
  finishReason: GeminiFinishReason;

  @ApiProperty({ description: 'Index of the candidate in the list of candidates' })
  index: number;

  @ApiProperty({ type: [GeminiSafetyRating], description: 'List of ratings for the safety of a response candidate' })
  safetyRatings: GeminiSafetyRating[];
}

export class GeminiUsageMetadata {
  @ApiProperty({ description: 'Number of tokens in the prompt' })
  promptTokenCount: number;

  @ApiProperty({ description: 'Total number of tokens across the generated candidates' })
  candidatesTokenCount: number;

  @ApiProperty({ description: 'Total token count for the generation request (prompt + candidates)' })
  totalTokenCount: number;
}

export class GeminiGenerateContentResponse {
  @ApiProperty({ type: [GeminiCandidate], description: 'Candidate responses from the model' })
  candidates: GeminiCandidate[];

  @ApiPropertyOptional({ type: GeminiUsageMetadata, description: 'Metadata on the generation requests token usage' })
  usageMetadata?: GeminiUsageMetadata;
}

export class GeminiModel {
  @ApiProperty({ description: 'The resource name of the Model' })
  name: string;

  @ApiProperty({ description: 'The human-readable name of the model' })
  displayName: string;

  @ApiProperty({ description: 'A short description of the model' })
  description: string;

  @ApiProperty({ description: 'Maximum number of input tokens allowed for this model' })
  inputTokenLimit: number;

  @ApiProperty({ description: 'Maximum number of output tokens available for this model' })
  outputTokenLimit: number;

  @ApiProperty({ type: [String], description: 'The model\'s supported generation methods' })
  supportedGenerationMethods: string[];

  @ApiPropertyOptional({ description: 'Controls the randomness of the output' })
  temperature?: number;

  @ApiPropertyOptional({ description: 'For Nucleus sampling' })
  topP?: number;

  @ApiPropertyOptional({ description: 'For Top-k sampling' })
  topK?: number;
}

export class GeminiListModelsResponse {
  @ApiProperty({ type: [GeminiModel], description: 'The returned Models' })
  models: GeminiModel[];

  @ApiPropertyOptional({ description: 'A token, which can be sent as pageToken to retrieve the next page' })
  nextPageToken?: string;
}

export class GeminiCountTokensRequest {
  @ApiProperty({ type: [GeminiContent], description: 'The input given to the model as a prompt' })
  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => GeminiContent)
  contents: GeminiContent[];
}

export class GeminiCountTokensResponse {
  @ApiProperty({ description: 'The number of tokens that the model tokenizes the prompt into' })
  totalTokens: number;
}